package com.jse.jakarta;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpCookie;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import com.jse.Fs;
import com.jse.Io;
import com.jse.Jse;
import com.jse.Strings;
import com.jse.api.Oss;
import com.jse.json.Json;
import com.jse.json.JsonObject;
import com.jse.json.XML;
import com.jse.web.Ctx;
import com.jse.web.Web;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part;

public class Tbl implements Ctx {
	
	private String method;
	private String path;
	private String suffix;
	private Map<String,HttpCookie> cookies;
	private String serverName;
	private String mapping;
	
	private JsonObject tbl;
	private HttpServletRequest req;
	private HttpServletResponse res;
	public Tbl(HttpServletRequest req,HttpServletResponse res) {
		this.req=req;
		this.res=res;
		method=req.getMethod();
		path=Strings.def(req.getServletPath(),req.getPathInfo());
		int suffixindex=path.lastIndexOf(".");
		suffix=suffixindex==-1?"":path.substring(suffixindex+1);
		serverName=req.getServerName();
	}

	@Override public Object req() {return req;}
	@Override public Object res() {return res;}
	
	@Override public int size() {return tbl().size();}
	@Override public boolean isEmpty() {return tbl().isEmpty();}
	@Override public boolean containsKey(Object key) {return tbl().containsKey(key);}
	@Override public boolean containsValue(Object value) {return tbl().containsValue(value);}
	@Override public Object get(Object key) {return tbl().get(key);}
	@Override public Object put(String key, Object value) {return tbl().put(key, value);}
	@Override public Object remove(Object key) {return tbl().remove(key);}
	@Override public void putAll(Map<? extends String, ? extends Object> m) {tbl().putAll(m);}
	@Override public void clear() {tbl().clear();}
	@Override public Set<String> keySet() {return tbl().keySet();}
	@Override public Collection<Object> values() {return tbl().values();}
	@Override public Set<Entry<String, Object>> entrySet() {return tbl().entrySet();}
	@Override public String toString() {return Json.toJson(tbl());}
	@Override public JsonObject tbl() {
		if(tbl!=null)return tbl;
		tbl=new JsonObject();
		try {parseTbl(req);} catch (IOException e) {e.printStackTrace();}
		return tbl;
	}
	
	private void parseTbl(HttpServletRequest req) throws IOException {
		if("GET".equals(method)) {
	        req.getParameterMap().forEach((k,v)->{
	        	tbl.add(k,v!=null?(v.length==1?v[0]:Arrays.asList(v)):v);
	        });
		}else if("POST".equals(method)) {
        	String reqcontentType=req.getContentType();
        	if(reqcontentType==null) {//没有body none
        	}else if(reqcontentType.startsWith("application/x-www-form-urlencoded")) {
        		req.getParameterMap().forEach((k,v)->{
    	        	tbl.add(k,v!=null?(v.length==1?v[0]:Arrays.asList(v)):v);
    	        });
            }else if(reqcontentType.startsWith("application/json")) {
            	tbl.putAll(Json.jsonObject(Io.readString(req.getReader())));
            }else if(reqcontentType.startsWith("application/xml")) {
            	tbl.putAll(XML.toJSONObject(Io.readString(req.getReader())));
            }else if(reqcontentType.startsWith("multipart/form-data")) {//上传
            	try {
					var d=LocalDateTime.now();
					Map<String,String> ups=new HashMap<>();
					for(Part part:req.getParts()) {
						if(part.getSubmittedFileName()!= null&&part.getSize()>0) {
							tbl.put(part.getName(),part);
							if(path.equals("/jse/up")||Web.upload_auto) {
								ups.put(part.getName(),null);
							}
						}else {
							if(part.getName().charAt(0)=='_'&&part.getName().endsWith("_")) {
								ups.put(part.getName(),req.getParameter(part.getName()));
							}else {
								tbl.put(part.getName(),req.getParameter(part.getName()));
							}
						}
					}
					for (String key : ups.keySet()) {
						if(key.charAt(0)!='_'&&!key.endsWith("_")) {//非_开头和结尾
							Part part=req.getPart(key);
							String filePath=ups.get(key);
							if(ups.containsKey("_"+key+"_")) {
								filePath=ups.get("_"+key+"_");
							}else if(ups.containsKey("_f_")) {
								filePath=ups.get("_f_");
							}else if(ups.containsKey("_path_")) {
								filePath=ups.get("_path_")+part.getSubmittedFileName();
							}else {
								String suffix0=Strings.def(Fs.suffix(part.getSubmittedFileName()),"txt");
								filePath="/upload/"+Jse.name+"/"+
							d.getYear()+"/"+d.getMonthValue()+"/"+d.getDayOfMonth()+"/"+System.nanoTime()+"."+suffix0;//自动路径
							}
							tbl.put(key,filePath);
							String uploadpath=Jse.conf.get("web.upload",Jse.webapps());
							String abspath=uploadpath+filePath;
							Fs.mkdirs(abspath);
							part.write(abspath);//把文件写到指定路径
							if(Jse.conf.containsKey("ali.oss.bucket")) {
								if(filePath.charAt(0)=='/')filePath=filePath.substring(1);
								String oss=Oss.upload(Map.of("path",filePath,"file",abspath));
								System.out.println("oss up file "+oss);
								Jse.conf.add("tempfile",abspath);
//								tbl.put(key,oss);
								Fs.delete(Path.of(abspath));
							}
							part.delete();//删除文件项的基础存储，包括删除任何关联的临时磁盘文件。
						}
					}
				} catch (IOException|ServletException e) {e.printStackTrace();
				}
            }else {
            	tbl.put("body",Io.readString(req.getReader()));
            }
        }
	}
	public String getPath() {return path;}
	public String getSuffix() {return suffix;}
	public JsonObject getTbl() {return tbl;}
	public String getServerName() {return serverName;}
	
	@Override public String getMethod() {return method;}
	@Override public String getHeader(String name) {return req.getHeader(name);}
	@Override public void setAttribute(String name, Object value) {req.setAttribute(name, value);}
	@Override public Object getAttribute(String name) {return req.getAttribute(name);}
	@Override public Object getSession() {return req.getSession();}
	@Override public String getRequestedSessionId() {return req.getRequestedSessionId();}

	@Override
	public Map<String, HttpCookie> cookies() {
		if(cookies!=null)return cookies;
		cookies=new HashMap<>();
		Arrays.stream(req.getCookies()).forEach(c->{
			HttpCookie cookie=new HttpCookie(c.getName(),c.getValue());
			if(c.getSecure())cookie.setSecure(true);
			if(c.getDomain()!=null)cookie.setDomain(c.getDomain());
			if(c.getMaxAge()>-1)cookie.setMaxAge(c.getMaxAge());
			if(c.getPath()!=null)cookie.setPath(c.getPath());
			if(c.isHttpOnly())cookie.setHttpOnly(true);
			cookies.put(c.getName(),cookie);
		});
		return cookies;
	}
	
	public void addCookie(HttpCookie hcookie) {
		Cookie cookie=new Cookie(hcookie.getName(),hcookie.getValue());
		if(hcookie.getSecure())cookie.setSecure(true);
		if(hcookie.getDomain()!=null)cookie.setDomain(hcookie.getDomain());
		if(hcookie.getMaxAge()>-1)cookie.setMaxAge((int)hcookie.getMaxAge());
		if(hcookie.getPath()!=null)cookie.setPath(hcookie.getPath());
		if(hcookie.isHttpOnly())cookie.setHttpOnly(true);
		res.addCookie(cookie);
	}

	@Override public String reqcontentType() {return req.getContentType();}
	@Override public String getContentType() {return res.getContentType();}
	@Override public void setContentType(String type) {res.setContentType(type);}
	@Override public void setStatus(int status) {res.setStatus(status);}
	@Override public void sendError(int status, String msg){try{res.sendError(status, msg);}catch(IOException e){e.printStackTrace();}}
	@Override public OutputStream getResponseBody(){try {return res.getOutputStream();}catch(IOException e){return null;}}
	@Override public boolean isCommitted() {return res.isCommitted();}
	@Override public void sattr(String name, Object value) {req.getSession().setAttribute(name, value);}
	@Override public Object sattr(String name) {return req.getSession().getAttribute(name);}
	@Override public void sattr() {req.getSession().invalidate();}

	@Override public Map<String, Object> sessionMap() {
		Map<String, Object> m=new HashMap<>();
		var s=req.getSession();
		s.getAttributeNames().asIterator().forEachRemaining(n->{
			m.put(n,s.getAttribute(n));
		});
		return m;
	}

	@Override public void setHeader(String name, String value) {res.setHeader(name, value);}

	@Override public InputStream getRequestBody(){try{return req.getInputStream();}catch(IllegalStateException e) {return null;}
	catch(Exception e){throw new RuntimeException(e);}}

	@Override
	public Map<String, Object> attr() {
		Map<String, Object> m=new HashMap<>();
		req.getAttributeNames().asIterator().forEachRemaining(n->{
			m.put(n,req.getAttribute(n));
		});
		return m;
	}

	@Override public int httpStatus() {return res.getStatus();}
	@Override public void sendResponseHeaders(int code, long len) throws IOException {
		res.setStatus(code);
		res.setContentLengthLong(len==0?-1:len);
	}
	@Override public String getRemoteAddr() {return req.getRemoteAddr();}
	@Override public String getServletPath() {return req.getServletPath();}
	@Override public String getMapping(){if(mapping==null)mapping=Web.mapping(path, suffix, tbl());
	return mapping;}

	@Override
	public String getQueryString() {return req.getQueryString();}


}
